package com.owulia.leetcode.string;

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

public class LongestNotRepeatSubstring {


    public static int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> characterIntegerMap = new LinkedHashMap<>();
        int lengthTemp = 0;
        int length = 0;
        for (int i = 0; i < s.length(); i++) {
            Character c = s.charAt(i);
            if (characterIntegerMap.containsKey(c)) {
                i = characterIntegerMap.get(c) + 1;
                lengthTemp = 1;
                characterIntegerMap.remove(c);
            } else {
                characterIntegerMap.put(c, i);
                lengthTemp++;
            }
            if (lengthTemp > length) {
                length = lengthTemp;
            }
        }
        return length;
    }

    public static void main(String[] args) {
        System.out.println(lengthOfLongestSubstringV2("abcabcbb"));
    }


    /**
     * 方法一：滑动窗口
     思路和算法

     我们先用一个例子来想一想如何在较优的时间复杂度内通过本题。

     我们不妨以示例一中的字符串 abcabcbb 为例，找出 从每一个字符开始的，不包含重复字符的最长子串，那么其中最长的那个字符串即为答案。对于示例一中的字符串，我们列举出这些结果，其中括号中表示选中的字符以及最长的字符串：

     以 (a)bcabcbb} 开始的最长字符串为 (abc)abcbb}；
     以 a(b)cabcbb} 开始的最长字符串为 a(bca)bcbb}；
     以 ab(c)abcbb} 开始的最长字符串为 ab(cab)cbb}；
     以 abc(a)bcbb} 开始的最长字符串为 abc(abc)bb}；
     以 abca(b)cbb} 开始的最长字符串为 abca(bc)bb}；
     以 abcab(c)bb} 开始的最长字符串为 abcab(cb)b}；
     以 abcabc(b)b} 开始的最长字符串为 abcabc(b)b}；
     以 abcabcb(b)} 开始的最长字符串为 abcabcb(b)}。
     发现了什么？如果我们依次递增地枚举子串的起始位置，那么子串的结束位置也是递增的！这里的原因在于，假设我们选择字符串中的第 kk 个字符作为起始位置，并且得到了不包含重复字符的最长子串的结束位置为 r_krk
     。那么当我们选择第 k+1k+1 个字符作为起始位置时，首先从 k+1k+1 到 r_kr
     ​
     k 的字符显然是不重复的，并且由于少了原本的第 kk 个字符，我们可以尝试继续增大 r_kr
     k
     ​
     ，直到右侧出现了重复字符为止。

     这样以来，我们就可以使用「滑动窗口」来解决这个问题了：

     我们使用两个指针表示字符串中的某个子串（的左右边界）。其中左指针代表着上文中「枚举子串的起始位置」，而右指针即为上文中的 r_kr
     k
     ​
     ；

     在每一步的操作中，我们会将左指针向右移动一格，表示 我们开始枚举下一个字符作为起始位置，然后我们可以不断地向右移动右指针，但需要保证这两个指针对应的子串中没有重复的字符。在移动结束后，这个子串就对应着 以左指针开始的，不包含重复字符的最长子串。我们记录下这个子串的长度；

     在枚举结束后，我们找到的最长的子串的长度即为答案。

     判断重复字符

     在上面的流程中，我们还需要使用一种数据结构来判断 是否有重复的字符，常用的数据结构为哈希集合（即 C++ 中的 std::unordered_set，Java 中的 HashSet，Python 中的 set, JavaScript 中的 Set）。在左指针向右移动的时候，我们从哈希集合中移除一个字符，在右指针向右移动的时候，我们往哈希集合中添加一个字符。

     至此，我们就完美解决了本题。

     作者：LeetCode-Solution
     链接：https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/wu-zhong-fu-zi-fu-de-zui-chang-zi-chuan-by-leetc-2/
     来源：力扣（LeetCode）
     著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
     * @param s
     * @return
     */
    public static int lengthOfLongestSubstringV2(String s) {
        Set<Character> repeatChars = new HashSet<>();
        int length = s.length();
        int ans =0;
        for (int i = 0, j = 0; i < length; i++) {
            if (i > 0) {
                repeatChars.remove(s.charAt(i - 1));
            }
            for (; j < s.length() && !repeatChars.contains(s.charAt(j)); j++) {
                repeatChars.add(s.charAt(j));
            }
            ans=Math.max(ans,(j-i));
        }
        return ans;
    }
}
